import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;

public class FeistelFPECipher
{

    private static final int TOTAL_DIGITS = 19;  // 固定处理19位数字
    private static final int R = 16;        // Feistel轮次（建议至少8轮）
    private static final int ROUNDS = 16;   // 加密轮次（建议至少16轮）

    /**
     * 加密方法
     *
     * @param plainText 19位纯数字明文（如"1234567890123456789"）
     * @param key       密钥（建议至少16字节）
     * @return 19位密文数字
     */
    public static String encrypt(String plainText, byte[] key) throws Exception
    {
        // validateInput(plainText);
        String numStr = plainText;
        int split = (TOTAL_DIGITS + 1) / 2; // 初始拆分为左10位、右9位

        for (int i = 0; i < ROUNDS; i++) {
            String[] parts = splitNumber(numStr, split);
            String L = parts[0], R = parts[1];
            String F = feistelFunction(R, i, key, R.length()); // 生成与右半部分位数一致的F
            String newL = R;
            String newR = xor(L, F, L.length());              // 左半部分位数固定
            numStr = combineParts(newL, newR);
            split = newL.length(); // 下一轮拆分位数调整为当前左半部分长度
        }

        return numStr;
    }

    /**
     * 解密方法
     *
     * @param cipherText 19位纯数字密文
     * @param key        密钥（需与加密时相同）
     * @return 19位明文数字
     */
    public static String decrypt(String cipherText, byte[] key) throws Exception
    {
        // validateInput(cipherText);
        String numStr = cipherText;
        int split = (TOTAL_DIGITS + 1) / 2; // 初始拆分为左10位、右9位

        for (int i = ROUNDS - 1; i >= 0; i--) {
            String[] parts = splitNumber(numStr, split);
            String L = parts[0], R = parts[1];
            String F = feistelFunction(L, i, key, L.length()); // 生成与左半部分位数一致的F
            String newR = L;
            String newL = xor(R, F, R.length());              // 右半部分位数固定
            numStr = combineParts(newL, newR);
            split = newL.length(); // 下一轮拆分位数调整为当前左半部分长度
        }

        return numStr;
    }

    // ------------------------ 核心辅助方法 ------------------------

    /**
     * Feistel轮函数（生成固定位数的混淆值）
     */
    private static String feistelFunction(String data, int round, byte[] key, int digits) throws Exception
    {
        byte[] roundKey = generateRoundKey(key, round);
        BigInteger dataNum = new BigInteger(data);

        // 将数据和轮密钥混合计算哈希
        byte[] dataBytes = dataNum.toByteArray();
        byte[] combined = new byte[dataBytes.length + roundKey.length];
        System.arraycopy(dataBytes, 0, combined, 0, dataBytes.length);
        System.arraycopy(roundKey, 0, combined, dataBytes.length, roundKey.length);

        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] hash = md.digest(combined);
        BigInteger hashValue = new BigInteger(1, hash);

        // 取模并格式化为指定位数
        var modulus = BigInteger.TEN.pow(digits);
        return String.format("%0" + digits + "d", hashValue.mod(modulus));
    }

    /**
     * 生成轮密钥（16字节）
     */
    private static byte[] generateRoundKey(byte[] masterKey, int round) throws Exception
    {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(masterKey);

        // 将轮次转换为4字节（大端序）
        byte[] roundBytes = new byte[4];
        roundBytes[0] = (byte) (round >> 24);
        roundBytes[1] = (byte) (round >> 16);
        roundBytes[2] = (byte) (round >> 8);
        roundBytes[3] = (byte) round;

        md.update(roundBytes);
        return Arrays.copyOf(md.digest(), 16); // 截取前16字节
    }

    /**
     * 按固定位数异或（保留前导零）
     */
    private static String xor(String a, String b, int length)
    {
        BigInteger aNum = new BigInteger(a);
        BigInteger bNum = new BigInteger(b);
        BigInteger result = aNum.xor(bNum);
        return String.format("%0" + length + "d", result);
    }

    /**
     * 拆分数字字符串为左右两部分
     */
    private static String[] splitNumber(String numStr, int split)
    {
        return new String[]{
                numStr.substring(0, split),
                numStr.substring(split)
        };
    }

    /**
     * 合并左右部分为完整字符串
     */
    private static String combineParts(String left, String right)
    {
        return left + right;
    }

    /**
     * 校验输入合法性
     */
    private static void validateInput(String input)
    {
        if (input == null || !input.matches("\\d{" + TOTAL_DIGITS + "}")) {
            throw new IllegalArgumentException("输入必须为" + TOTAL_DIGITS + "位纯数字");
        }
    }

    // ------------------------ 测试用例 ------------------------
    public static void main(String[] args) throws Exception
    {
        // 生成随机密钥（建议实际使用时使用SecureRandom）
        byte[] key = new byte[16];
        new SecureRandom().nextBytes(key);

        String plainText = "1234567890123456789";
        System.out.println("原文: " + plainText);

        // 加密
        String cipherText = encrypt(plainText, key);
        System.out.println("加密后: " + cipherText);
        System.out.println("长度验证: " + (cipherText.length() == TOTAL_DIGITS));

        // 解密
        String decryptedText = decrypt(cipherText, key);
        System.out.println("解密后: " + decryptedText);
        System.out.println("解密验证: " + plainText.equals(decryptedText));
    }
}